package com.derbysoft.dswitch.adapter.ihg.dswitch2;

import com.derbysoft.dswitch.adapter.ihg.common.domain.OperationType;
import com.derbysoft.dswitch.adapter.ihg.common.domain.ReservationLog;
import com.derbysoft.dswitch.adapter.ihg.common.domain.TransactionLog;
import com.derbysoft.dswitch.adapter.ihg.common.log.*;
import com.derbysoft.dswitch.adapter.ihg.common.repository.ReservationLogRepository;
import com.derbysoft.dswitch.adapter.ihg.common.repository.TransactionLogRepository;
import com.derbysoft.dswitch.adapter.ihg.common.utils.DateTimeUtils;
import com.derbysoft.dswitch.adapter.ihg.dswitch2.service.handler.RemoteServiceHandler;
import com.derbysoft.dswitch.adapter.ihg.dswitch2.service.impl.ReservationIDUtils;
import com.derbysoft.remote.dswitch.core.GenericRQ;
import com.derbysoft.remote.dswitch.core.GenericRS;
import com.derbysoft.remote.dswitch.generic.constants.TransactionActionType;
import com.derbysoft.remote.dswitch.hotel.adapter.hotelprovider.HotelProviderWebService;
import com.derbysoft.remote.dswitch.hotel.dto.*;
import com.derbysoft.remote.dswitch.hotel.dto.sync.availstatuschange.SyncAvailStatusChangeRQ;
import com.derbysoft.remote.dswitch.hotel.dto.sync.availstatuschange.SyncAvailStatusChangeRS;
import com.derbysoft.remote.dswitch.hotel.dto.sync.ratechange.SyncRateChangeRQ;
import com.derbysoft.remote.dswitch.hotel.dto.sync.ratechange.SyncRateChangeRS;
import com.derbysoft.remote.dswitch.hotel.dto.sync.rateplanchange.SyncRatePlanChangeRQ;
import com.derbysoft.remote.dswitch.hotel.dto.sync.rateplanchange.SyncRatePlanChangeRS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

/**
 * Created by: jason
 * Date: 2012-06-01
 */
@Service("ds2HotelProviderWebService")
public class HotelProviderServiceProxy implements HotelProviderWebService {

    @Autowired
    @Qualifier("ds2HotelAvailabilityHandler")
    private RemoteServiceHandler<GenericRQ<HotelAvailRQ>, GenericRS<HotelAvailRS>> availabilityHandler;

    @Autowired
    @Qualifier("ds2HotelMultiAvailabilityHandler")
    private RemoteServiceHandler<GenericRQ<HotelMultiAvailRQ>, GenericRS<HotelMultiAvailRS>> ds2HotelMultiAvailabilityHandler;

    @Autowired
    @Qualifier("ds2HotelPreviewHandler")
    private RemoteServiceHandler<GenericRQ<HotelReservationRQ>, GenericRS<HotelReservationRS>> ds2HotelPreviewHandler;

    @Autowired
    @Qualifier("ds2HotelReservationHandler")
    private RemoteServiceHandler<GenericRQ<HotelReservationRQ>, GenericRS<HotelReservationRS>> ds2HotelReservationHandler;

    @Autowired
    @Qualifier("ds2HotelModifyReservationHandler")
    private RemoteServiceHandler<GenericRQ<HotelReservationRQ>, GenericRS<HotelReservationRS>> ds2HotelModifyReservationHandler;

    @Autowired
    @Qualifier("ds2HotelCancelHandler")
    private RemoteServiceHandler<GenericRQ<CancelRQ>, GenericRS<CancelRS>> ds2HotelCancelHandler;

    @Autowired
    private TransactionLogRepository transactionLogRepository;

    @Autowired
    private ReservationLogRepository reservationLogRepository;

    @Autowired
    @Qualifier("taskExecutor")
    private Executor executor;

    @Override
    public GenericRS<HotelAvailRS> getAvailability(GenericRQ<HotelAvailRQ> request) {
        setAvailLog(request);

        GenericRS<HotelAvailRS> hotelAvailRSGenericRS = availabilityHandler.invoke(request);

        PerformanceLogger.save(executor);
        saveLogs();
        return hotelAvailRSGenericRS;
    }

    @Override
    public GenericRS<HotelMultiAvailRS> getMultiAvailability(GenericRQ<HotelMultiAvailRQ> request) {
        setMultiAvailLog(request);

        GenericRS<HotelMultiAvailRS> multiAvailRSGenericRS = ds2HotelMultiAvailabilityHandler.invoke(request);

        PerformanceLogger.save(executor);
        saveLogs();
        return multiAvailRSGenericRS;
    }

    @Override
    public GenericRS<HotelReservationRS> makeReservation(GenericRQ<HotelReservationRQ> request) {
        PerformanceLogger.init();
        GenericRS<HotelReservationRS> reservationRS;
        setReservationLog(request);
        if (request.getPayload().getResActionType() == TransactionActionType.INITIATE) {
            PerformanceLogger.append(LogBody.REQUEST_TYPE, OperationType.HOTEL_PREVIEW.name());
            reservationRS = ds2HotelPreviewHandler.invoke(request);
        } else if (request.getPayload().getResActionType() == TransactionActionType.BOOK) {
            reservationRS = ds2HotelReservationHandler.invoke(request);
            PerformanceLogger.append(LogBody.REQUEST_TYPE, OperationType.HOTEL_RESERVATION.name());
        } else {
            reservationRS = ds2HotelModifyReservationHandler.invoke(request);
            PerformanceLogger.append(LogBody.REQUEST_TYPE, OperationType.HOTEL_MODIFY_RESERVATION.name());
        }
        PerformanceLogger.save(executor);
        saveLogs();
        return reservationRS;
    }

    @Override
    public GenericRS<CancelRS> cancelReservation(GenericRQ<CancelRQ> request) {
        PerformanceLogger.init();

        PerformanceLogger.append(LogBody.REQUEST_TYPE, OperationType.CANCEL_RESERVATION.name());
        setPerformanceLogHeader(request);
        PerformanceLogger.append(LogBody.ERSP_NUMBER, ReservationIDUtils.getErspNo(request.getPayload().getHotelReservationIDs()).getResIDValue());

        GenericRS<CancelRS> cancelRS = ds2HotelCancelHandler.invoke(request);
        PerformanceLogger.save(executor);
        saveLogs();
        return cancelRS;
    }

    private void saveLogs() {
        TransactionLog transactionLog = TransactionLogger.getAvailLogger().get();
        Set<LogDetailPair> logDetailPairs = TransactionLogger.getLogDetails().get();
        if (transactionLog != null) {
            executor.execute(new TransactionLogTask(transactionLogRepository, transactionLog, logDetailPairs));
            TransactionLogger.cleanAvailLogger();
        }
        ReservationLog reservationLog = TransactionLogger.getReservationLogger().get();
        if (reservationLog != null) {
            executor.execute(new ReservationLogTask(reservationLogRepository, reservationLog, logDetailPairs));
            TransactionLogger.cleanReservationLogger();
        }
        TransactionLogger.cleanLogDetails();
    }

    private void setReservationLog(GenericRQ<HotelReservationRQ> request) {
        ReservationRoomStayDTO reservationRoomStayDTO = request.getPayload().getHotelReservation().getRoomStays().get(0);
        setPerformanceLogHeader(request);
        PerformanceLogger.append(LogBody.DERBY_HOTEL_CODES, request.getPayload().getHotelReservation().getHotelRef().getCode());
        PerformanceLogger.append(LogBody.CHECK_IN, DateTimeUtils.formatDate(reservationRoomStayDTO.getStayDateRange().getCheckInDate()));
        PerformanceLogger.append(LogBody.CHECK_OUT, DateTimeUtils.formatDate(reservationRoomStayDTO.getStayDateRange().getCheckOutDate()));
        PerformanceLogger.append(LogBody.RATE_PLAN, reservationRoomStayDTO.getRatePlanCode());
        PerformanceLogger.append(LogBody.ROOM_TYPE, reservationRoomStayDTO.getRoomTypeCode());
        PerformanceLogger.append(LogBody.NUMBER_OF_UNIT, String.valueOf(reservationRoomStayDTO.getNumberOfUnits()));
        PerformanceLogger.append(LogBody.ADULT, String.valueOf(reservationRoomStayDTO.getAdultCount()));
        PerformanceLogger.append(LogBody.CHILD, String.valueOf(reservationRoomStayDTO.getChildCount()));
        PerformanceLogger.append(LogBody.ERSP_NUMBER, ReservationIDUtils.getErspNo(request.getPayload().getHotelReservationIDs()).getResIDValue());
    }


    private void setMultiAvailLog(GenericRQ<HotelMultiAvailRQ> request) {
        PerformanceLogger.init();
        PerformanceLogger.append(LogBody.REQUEST_TYPE, OperationType.HOTEL_MULTI_AVAIL.name());
        setPerformanceLogHeader(request);
        PerformanceLogger.append(LogBody.DERBY_HOTEL_CODES, getHotelCodes(request.getPayload().getHotels()));
        PerformanceLogger.append(LogBody.CHECK_IN, DateTimeUtils.formatDate(request.getPayload().getStayDateRange().getCheckInDate()));
        PerformanceLogger.append(LogBody.CHECK_OUT, DateTimeUtils.formatDate(request.getPayload().getStayDateRange().getCheckOutDate()));
        PerformanceLogger.append(LogBody.NUMBER_OF_UNIT, String.valueOf(request.getPayload().getRoomStayCandidates().getNumberOfUnits()));
        PerformanceLogger.append(LogBody.ADULT, String.valueOf(request.getPayload().getRoomStayCandidates().getAdultCount()));
        PerformanceLogger.append(LogBody.CHILD, String.valueOf(request.getPayload().getRoomStayCandidates().getChildCount()));
    }

    private void setAvailLog(GenericRQ<HotelAvailRQ> request) {
        PerformanceLogger.init();
        PerformanceLogger.append(LogBody.REQUEST_TYPE, OperationType.HOTEL_AVAIL.name());
        setPerformanceLogHeader(request);
        PerformanceLogger.append(LogBody.DERBY_HOTEL_CODES, getHotelCodes(request.getPayload().getAvailCriteria()));
        PerformanceLogger.append(LogBody.CHECK_IN, DateTimeUtils.formatDate(request.getPayload().getAvailCriteria().getStayDateRange().getCheckInDate()));
        PerformanceLogger.append(LogBody.CHECK_OUT, DateTimeUtils.formatDate(request.getPayload().getAvailCriteria().getStayDateRange().getCheckOutDate()));
        PerformanceLogger.append(LogBody.NUMBER_OF_UNIT, String.valueOf(request.getPayload().getAvailCriteria().getRoomStayCandidates().getNumberOfUnits()));
        PerformanceLogger.append(LogBody.ADULT, String.valueOf(request.getPayload().getAvailCriteria().getRoomStayCandidates().getAdultCount()));
        PerformanceLogger.append(LogBody.CHILD, String.valueOf(request.getPayload().getAvailCriteria().getRoomStayCandidates().getChildCount()));
    }

    private String getHotelCodes(List<HotelRefDTO> hotels) {
        StringBuilder builder = new StringBuilder();
        for (HotelRefDTO hotel : hotels) {
            builder.append(hotel.getCode()).append(" ");
        }
        return builder.toString();
    }

    private void setPerformanceLogHeader(GenericRQ request) {
        PerformanceLogger.append(LogBody.TASK_ID, request.getHeader().getTaskId());
        PerformanceLogger.append(LogBody.CHANNEL, request.getHeader().getSource().getUniqueID().getId());
        PerformanceLogger.append(LogBody.PROVIDER_CHAIN, "ihg");
    }

    private String getHotelCodes(HotelAvailCriteriaDTO availCriteria) {
        return availCriteria.getHotelRef().getCode();
    }

    @Override
    public GenericRS<CancelNotifyRS> notifyCancel(GenericRQ<CancelNotifyRQ> request) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<HotelMultiRateRangeRS> getMultiRateRange(GenericRQ<HotelMultiRateRangeRQ> request) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<HotelRateRangeRS> getRateRange(GenericRQ<HotelRateRangeRQ> request) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<HotelReservationNotifyRS> notifyReservation(GenericRQ<HotelReservationNotifyRQ> request) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<SyncAvailStatusChangeRS> syncAvailStatusChange(GenericRQ<SyncAvailStatusChangeRQ> request) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<SyncRateChangeRS> syncRateChange(GenericRQ<SyncRateChangeRQ> request) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<SyncRatePlanChangeRS> syncRatePlanChange(GenericRQ<SyncRatePlanChangeRQ> request) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<QueryHotelReservationAuditRS> query(GenericRQ<QueryHotelReservationAuditRQ> queryRQ) {
        throw new UnsupportedOperationException();
    }

    @Override
    public GenericRS<QueryReservationTransactionStatusRS> queryReservationTransactionStatus(GenericRQ<QueryReservationTransactionStatusRQ> queryRQ) {
        throw new UnsupportedOperationException();
    }
}
